package com.elephant.core.socket.socketforxml;

/**
 * @author hai
 * @description
 * @date 15:24 2018/8/31
 */

import com.elephant.core.utils.MessageXmlUtil;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.*;

/**
 * 学生行为统计的socket server端
 * @author maojd
 *
 */
public class Server {

    private static String ip = "127.0.0.1";
    private static int port = 8899;
    private static Map<String, Socket> clientMap = new HashMap<String, Socket>();
    private static Map<String, Task> clientThreadMap = new HashMap<String, Task>();
    private static int msgNO = 1;
    private static String endFlag = "</MESSAGE>";
    private static int count = 3;

    private static final ExecutorService POOL;

    static {
        ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
                .setNameFormat("demo-pool-%d").build();

        //Common Thread Pool
        POOL = new ThreadPoolExecutor(5, 200,
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());


    }


    public static void main(String[] args) throws IOException {
        //假的定时器，模拟学生行为，给所有的客户端发送学生事件。
        setTimeTaskMsg();


        // 为了简单起见，所有的异常信息都往外抛
        // 定义一个ServerSocket监听在端口8899上
        ServerSocket server = new ServerSocket(port);
        while (true) {
            Socket socket = server.accept();

            Task t = new Task(socket);
            POOL.submit(t);

            clientMap.put(socket.toString(), socket);
            clientThreadMap.put(socket.toString(), t);

            System.out.println();
            System.out.println("server.accept new client socket.");
            System.out.println(socket.toString());
            System.out.println(clientMap.keySet());

        }

    }

    /**
     * xml格式说明：
     * NAME = ACTIVE心跳 EVENT学生事件
     * NO 消息序列号
     * TYPE ESP相应， REQ 请求
     * CONTENT 没有实际意义，为了避免 <MESSAGE NAME="EVENT" NO="6"/> xml没有结束标签
     * EventNo 1 进入教室 2 离开教室 3 吃饭 4出校门 5进入校门
     * EventName 事件名称
     * StuNo 学号
     *
     * 定时任务，模拟发送学生事件和心跳。
     */
    static void setTimeTaskMsg(){

        //org.apache.commons.lang3.concurrent.BasicThreadFactory
        ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1,
                new BasicThreadFactory.Builder().namingPattern("example-schedule-pool-%d").daemon(true).build());
        executorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                if(msgNO % count == 0 ){
                    //两次心跳，一次事件
                    MessageBean msg = new MessageBean();


                    /**
                     * <MESSAGE NAME="EVENT" NO="6" TYPE="RSP">
                     *   <CONTENT>NULL</CONTENT>
                     *   <EventNo>3</EventNo>
                     *   <StuNo>001003</StuNo>
                     *   <EventName>学生吃饭了</EventName>
                     *   <MobilePhone>1383838438</MobilePhone>
                     * </MESSAGE>
                     */
                    msg.setName("EVENT");
                    msg.setNo(String.valueOf(msgNO));
                    msg.setType("RSP");

                    msg.setContent("NULL");
                    msg.setEventNo("3");
                    msg.setStuNo("001003");
                    msg.setEventName("学生吃饭了");
                    msg.setMobilePhone("1383838438");

                    System.out.println();
                    System.out.println("server sendMsg.  str =\n" +  MessageXmlUtil.toXml(msg) + "\n");

                    for(String key:clientThreadMap.keySet()){
                        Task tTemp = clientThreadMap.get(key);
                        tTemp.sendMsg(MessageXmlUtil.toXml(msg));
                    }
                }else{
                    //两次心跳，一次事件
                    MessageBean msg = new MessageBean();
                    msg.setName("ACTIVE");
                    msg.setNo(String.valueOf(msgNO));
                    msg.setType("RSP");

                    msg.setContent("NULL");
                    System.out.println(System.currentTimeMillis());
                    System.out.println("server sendMsg.  str =\n" +  MessageXmlUtil.toXml(msg) + "\n");
                    for(String key:clientThreadMap.keySet()){
                        Task tTemp = clientThreadMap.get(key);
                        tTemp.sendMsg(MessageXmlUtil.toXml(msg));
                    }
                }

                msgNO++;
            }
        }, 0 , 3*1000, TimeUnit.MILLISECONDS);

    }

    static class Task implements Runnable {
        private Socket socket;

        public Task(Socket socket) {
            this.socket = socket;
        }

        @Override
        public void run() {
            try {

                handleSocket();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        /**
         * 跟客户端Socket进行通信
         *
         * @throws Exception
         */
        private void handleSocket() throws Exception {
            System.out.println();
            System.out.println("server handleSocket");
            BufferedReader br = new BufferedReader(new InputStreamReader(
                    socket.getInputStream()));
            StringBuffer sb = new StringBuffer();
            String temp;
            int index;
            while ((temp = br.readLine()) != null) {
                if(isEndMsg(temp)){
                    //一直等待处理客户端结果。
                    doRes(sb.toString());
                    sb = new StringBuffer();
                }else{
                    sb.append(temp);
                }
            }

            //到这里就说明客户端断开了。
            clientMap.remove(socket.toString());
            clientThreadMap.remove(socket.toString());

            System.out.println();
            System.out.println("socket disconnected");
            System.out.println(clientMap.keySet());
        }

        private void sendMsg(String str){
            Writer writer;
            try {
                writer = new OutputStreamWriter(socket.getOutputStream());
                str = str + "\n";
                writer.write(str);
                writer.flush();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }

        private void doRes(String str) {
            System.out.println();
            System.out.println("server doRes, 处理客户端的请求。 如果为心跳不处理，如果为认证请求，则验证密码是否合法等等。 str=\n" + str);
        }

        /**
         * 判断是否为 message的结束标识
         * @param str
         * @return
         */
        private static boolean isEndMsg(String str){
            if(StringUtils.isNotBlank(str) && str.endsWith(endFlag)){
                return true;
            }else{
                return false;
            }
        }

    }
}

